Pattern Forms
This page describes all the forms that patterns can take in Cyclone. For each pattern form, you need to know:
- The syntax
- The types of expressions it can match against (to avoid a compile-time error)
- The expressions the pattern matches against (other expressions cause a match failure)
- The bindings the pattern introduces, if any.
There is one compile-time rule that is the same for all forms: All variables (and type variables) in a pattern must be distinct. For example, “let Pair(fst,fst) = pr;” is not allowed.
You may want to read the descriptions for variable and struct patterns first because we have already explained their use informally.
Variable patterns
- Syntax: an identifer
- Types for match: all types
- Expressions matched: all expressions
- Bindings introduced: the identifier is bound to the expression being matched
Wildcard patterns
- Syntax: _ (underscore, note this use is completely independent of _ for [type inference][13])
- Type for match: all types
- Expressions matched: all expressions
- Bindings introduced: none. Hence it is like a variable pattern that uses a fresh identifier. Using _ is better style because it indicates the value matched is not used. Notice that “let _ = e;” is equivalent to e.
As patterns
- Syntax: x as p where x is an identifier and p is a pattern.
- Types for match: all types
- Expressions matched: all expressions
- Bindings introduced: if the expression matches the pattern p, then its value is bound to x. Thus, a variable pattern is simply shorthand for “x as _”.
Reference patterns
- Syntax: *x (i.e., the * character followed by an identifier)
- Types for match: all types
- Expressions matched: all expressions. (Very subtle notes: Currently, reference patterns may only appear inside of other patterns so that the compiler can determine the region for the pointer type assigned to x. They also may not occur under a datatype pattern that has existential types unless there is a pointer pattern in-between.)
- Bindings introduced: x is bound to the address of the
expression being matched. Hence if matched against a value of
type t in region
r, the type of x is t@
r.
Numeric constant patterns
- Syntax: An int, char, or float constant
- Types for match: numeric types
- Expressions matched: numeric values such that == applied to the value and the pattern yields true. (Standard C numeric promotions apply. Note that comparing floating point values for equality is usually a bad idea.)
- Bindings introduced: none
NULL* constant patterns*
- Syntax: NULL
- Types for match: nullable pointer types, including ? types
- Expressions matched: NULL
- Bindings introduced: none
Enum patterns
- Syntax: an enum constant
- Types for match: the enum type containing the constant
- Expressions matched: the constant
- Bindings introduced: none
Tuple patterns
Syntax: $(p1,…,pn[,…]) where p1,…,pn are patterns. The trailing comma and ellipses (…) are optional.
Types for match: if no ellipses, then tuple types with exactly n fields, where pi matches the type of the tuple’s ith field. If the ellipses are present, then matches a tuple with at least n fields.
- Expressions matched: tuples where the ith field matches pi for i between 1 and n.
- Bindings introduced: bindings introduced by p1, …, pn.
Struct patterns
Syntax: There are three forms:
X(p1,…,pn[,…]) where X is the name of a struct with n fields and p1,…,pn are patterns. This syntax is shorthand for
X{.f1 = p1, ..., .fn = pn [,...]}
where fi is the ith field in X.X{.f1 = p1, ..., .fn = pn [,...]}
where the fields of X are f1, …, fn but not necessarily in that order{.f1 = p1, ..., .fn = pn [,...]}
which is the same as above except that struct name X has been omitted.
Types for match: struct X (or instantiations when struct X is polymorphic) such that pi matches the type of fi for i between 1 and n. If the ellipses are not present, then each member of the struct must have a pattern.
- Expressions matched: structs where the value in fi matches pi for i between 1 and n.
- Bindings introduced: bindings introduced by p1,…,pn
Tagged Union patterns
Syntax: There are two forms:
X{.fi = p}
where the members of X are f1, …, fn and fi is one of those members.{{.f1 = p
which is the same as above except that union name X has been omitted.
Types for match: union X (or instantiations when union X is polymorphic) such that p matches the type of fi.
- Expressions matched: tagged unions where the last member written was fi and the value of that member matches p.
- Bindings introduced: bindings introduced by p.
Pointer patterns
- Syntax: &p where p is a pattern
- Types for match: pointer types, including ? types. Also datatype Foo @ (or instantiations of it) when the pattern is &Bar(p1,…,pn) and Bar is a variant of datatype Foo and pi matches the type of the ith value carried by Bar.
- Expressions matched: non-null pointers where the value pointed to matches p. Note this explanation includes the case where the expression has type datatype Foo @ and the pattern is &Bar(p1,…,pn) and the current variant of the expression is “pointer to Bar”.
- Bindings introduced: bindings introduced by p
Datatype patterns
- Syntax: X if X is a variant that carries no values. Else X(p1,…,pn[,…]) where X is the name of a variant and p1, …, pn are patterns. As with tuple and struct patterns, the ellipses are optional.
- Types for match: datatype Foo (or instantiations of it).
- Expressions matched: If X is non-value-carrying, then X. If X is value-carrying, then values created from the constructor X such that pi matches the ith field.
- Bindings introduced: bindings introduced by p1,…,pn